package de.justphil.tcg.tcgserver.game.mechanics.property.properties;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.List;

import de.justphil.tcg.tcgserver.commons.exceptions.TCGException;
import de.justphil.tcg.tcgserver.game.mechanics.event.Event;
import de.justphil.tcg.tcgserver.game.mechanics.event.Scheduler;
import de.justphil.tcg.tcgserver.game.mechanics.game.Game;
import de.justphil.tcg.tcgserver.game.mechanics.monster.Monster;
import de.justphil.tcg.tcgserver.game.mechanics.monster.MonsterList;
import de.justphil.tcg.tcgserver.game.mechanics.property.Property;
import org.slf4j.Logger;
import static de.justphil.tcg.tcgserver.commons.logging.TCGLogging.*;

public class Paralyzed extends Property {

	private static final Logger log = gameLogger(Paralyzed.class);

	private static final double SLOW_FACTOR = 2.0;

	private boolean paralyzed = false;

	private Scheduler scheduler;

	/**
	 * Constructor
	 * 
	 * @param game
	 */
	public Paralyzed(Game game) {

		super(game);

		if (game != null) {
			this.scheduler = game.getScheduler();
		}
	}

	@Override
	public int getType() {

		return PROPERTY_PARALYZED;
	}

	@Override
	protected MonsterList handleEvent(Event event) {

		switch (event.getType()) {

		case Event.EVENT_MONSTERS_TURN:

			handleMonstersTurn((Monster) event.getData());
			break;

		case Event.EVENT_EVENT_SCHEDULED:

			handleEventScheduled((Event) event.getData());

			break;
		default:
			assert false;
			break;
		}
		return null;
	}

	@Override
	protected List<Integer> getListeningEventTypes() {

		List<Integer> types = super.getListeningEventTypes();
		types.add(Event.EVENT_EVENT_SCHEDULED);
		types.add(Event.EVENT_MONSTERS_TURN);

		return types;
	}

	@Override
	protected MonsterList handleActivation() {

		Event event = new Event(Event.EVENT_MONSTERS_TURN, getMonster());

		if (scheduler.getTimeToEvent(event) >= 0) {

			slowMonster();

			this.paralyzed = true;
		}
		return null;
	}

	@Override
	protected MonsterList handleDeactivation() {

		if (paralyzed) {

			unslowMonster();

			paralyzed = false;
		}

		return null;
	}

	private void handleMonstersTurn(Monster monster) {

		/*
		 * When the monster has turn it is'nt paralyzed any more and has to be
		 * paralyzed again.
		 */
		if (getMonster().equals(monster)) {

			this.paralyzed = false;
		}
	}

	private void handleEventScheduled(Event event) {

		if (!paralyzed && event.getType() == Event.EVENT_MONSTERS_TURN
				&& event.getData().equals(getMonster())) {

			slowMonster();

			this.paralyzed = true;
		}
	}

	private void slowMonster() {

		log.info("Paralyzed is slowing a Monster ({}).", new Object[] { getMonster() });

		Event monstersTurn = new Event(Event.EVENT_MONSTERS_TURN, getMonster());
		double scheduledTime = scheduler.getTimeToEvent(monstersTurn);

		if(!scheduler.unschedule(monstersTurn)){
			
			assert false;
		}
		try {
			scheduler.scheduleFirst(monstersTurn, scheduledTime * SLOW_FACTOR);
		} catch (TCGException e) {
			assert false : e.getMessage();
		}
	}

	private void unslowMonster() {

		log.info("Paralyzed is unslowing a Monster ({}).", new Object[] { getMonster() });

		Event monstersTurn = new Event(Event.EVENT_MONSTERS_TURN, getMonster());

		double scheduledTime = scheduler.getTimeToEvent(monstersTurn);
		scheduler.unschedule(monstersTurn);

		try {
			scheduler.scheduleFirst(monstersTurn, scheduledTime / SLOW_FACTOR);
		} catch (TCGException e) {
			assert false : e.getMessage();
		}
	}

	@Override
	public void writeData(Game game, DataOutput out) throws IOException {

		super.writeData(game, out);

		out.writeBoolean(paralyzed);
	}

	@Override
	public void readData(Game game, DataInput in) throws IOException {

		super.readData(game, in);

		paralyzed = in.readBoolean();

		scheduler = getScheduler();
	}

	@Override
	protected int[] getCounterProperties() {

		return new int[] { PROPERTY_LAZY, PROPERTY_SLEEPING };
	}
}
